//******************************************************************
//******************************************************************
//**********          ANts Peer To Peer Sources        *************
//
// ANts P2P realizes a third generation P2P net. It protects your
// privacy while you are connected and makes you not trackable, hiding
// your identity (ip) and crypting everything you are sending/receiving
// from others.

// Copyright (C) 2004  Roberto Rossi

// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.

// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.

// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.


package ants.p2p.security.sockets;

import java.net.*;
import java.io.*;
import javax.net.*;
import javax.net.ssl.*;

import ants.p2p.utils.addresses.InetAddressWatchdog;

public class SSLProvider {

  private static void doTunnelHandshake(Socket tunnel, String host, int port) throws
      IOException {
    OutputStream out = tunnel.getOutputStream();
    String msg = "CONNECT " + host + ":" + port + " HTTP/1.0\n"
        + "User-Agent: "
        + "Mozilla/5.0 (X11; U; Linux i686; en-US; rv:0.9.3) Gecko/20010801"//sun.net.www.protocol.http.HttpURLConnection.userAgent);
        + "\r\n\r\n";
    byte b[];
    try {
      /*
       * We really do want ASCII7 -- the http protocol doesn't change
       * with locale.
       */
      b = msg.getBytes("ASCII7");
    }
    catch (UnsupportedEncodingException ignored) {
      /*
       * If ASCII7 isn't there, something serious is wrong, but
       * Paranoia Is Good (tm)
       */
      b = msg.getBytes();
    }
    out.write(b);
    out.flush();

    /*
     * We need to store the reply so we can create a detailed
     * error message to the user.
     */
    byte reply[] = new byte[200];
    int replyLen = 0;
    int newlinesSeen = 0;
    boolean headerDone = false; /* Done on first newline */

    InputStream in = tunnel.getInputStream();
    boolean error = false;

    while (newlinesSeen < 2) {
      int i = in.read();
      if (i < 0) {
        throw new IOException("Unexpected EOF from proxy");
      }
      if (i == '\n') {
        headerDone = true;
        ++newlinesSeen;
      }
      else if (i != '\r') {
        newlinesSeen = 0;
        if (!headerDone && replyLen < reply.length) {
          reply[replyLen++] = (byte) i;
        }
      }
    }

    /*
     * Converting the byte array to a string is slightly wasteful
     * in the case where the connection was successful, but it's
     * insignificant compared to the network overhead.
     */
    String replyStr;
    try {
      replyStr = new String(reply, 0, replyLen, "ASCII7");
    }
    catch (UnsupportedEncodingException ignored) {
      replyStr = new String(reply, 0, replyLen);
    }

    /* We check for Connection Established because our proxy returns
     * HTTP/1.1 instead of 1.0 */
    //if (!replyStr.startsWith("HTTP/1.0 200")) {
    if (replyStr.toLowerCase().indexOf(
        "200 connection established") == -1) {
      throw new IOException("Unable to tunnel through "
                            + host + ":" + port
                            + ".  Proxy returns \"" + replyStr + "\"");
    }

    /* tunneling Handshake was successful! */
  }

  public static Socket getSSLSecuredSocket(String remoteAddress, int remotePort, boolean proxied) throws Exception{
    TrustManager[] trustAllCerts = new TrustManager[] {
        new X509TrustManager() {
      public java.security.cert.X509Certificate[] getAcceptedIssuers() {
        return null;
      }

      public void checkClientTrusted(
          java.security.cert.X509Certificate[] certs, String authType) {
      }

      public void checkServerTrusted(
          java.security.cert.X509Certificate[] certs, String authType) {
      }
    }
    } ;
    SSLContext sc = null;
    sc = SSLContext.getInstance("TLSv1");
    sc.init(null, trustAllCerts, new java.security.SecureRandom());
    if(!InetAddressWatchdog.getInstance().allowedAddress(InetAddress.getByName(remoteAddress).getHostAddress()))
      throw new Exception("Address is not allowed: "+remoteAddress);

    Socket socket;
    if(proxied){
      String tunnelHost = System.getProperty("https.proxyHost");
      int tunnelPort = Integer.getInteger("https.proxyPort").intValue();
      Socket tunnel = new Socket(tunnelHost, tunnelPort);
      tunnel.setKeepAlive(true);
      tunnel.setSoTimeout(5000);
      doTunnelHandshake(tunnel, InetAddress.getByName(remoteAddress).getHostAddress(), remotePort);
      socket = sc.getSocketFactory().createSocket(tunnel, InetAddress.getByName(remoteAddress).getHostAddress(), remotePort, true);
    }else{
      Socket raw = new Socket(InetAddress.getByName(remoteAddress), remotePort);
      raw.setKeepAlive(true);
      raw.setSoTimeout(5000);
      socket = sc.getSocketFactory().createSocket(raw, InetAddress.getByName(remoteAddress).getHostAddress(), remotePort, true);
    }
    socket.setKeepAlive(true);
    socket.setSoTimeout(5000);
    return socket;
  }

  /*public static void main(String args[]){
    try{
      ServerSocket ss = new ServerSocket(443);
      Socket s = ss.accept();
      InputStream is = s.getInputStream();
      byte[] bytes = new byte[1024];
      while(is.read(bytes) != -1){
        System.out.print(new String(bytes));
      }
      System.out.print(new String(bytes));
      System.out.print("END");
    }catch(Exception e){
      e.printStackTrace();
    }
  }*/
}
